package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/apache/rocketmq-client-go/v2"
	"github.com/apache/rocketmq-client-go/v2/primitive"
	"github.com/apache/rocketmq-client-go/v2/producer"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"math/rand"
	"mxshop_srvs/order_srv/global"
	"mxshop_srvs/order_srv/model"
	"mxshop_srvs/order_srv/proto"
	"time"
)

type OrderServer struct {
	proto.UnimplementedOrderServer
}

func GenerateOrderSn(userId int32) string {
	//订单号的生成规则
	/*
		年月日时分秒+用户id+2位随机数
	*/
	now := time.Now()
	rand.Seed(time.Now().UnixNano())
	orderSn := fmt.Sprintf("%d%d%d%d%d%d%d%d",
		now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Nanosecond(),
		userId, rand.Intn(90)+10,
	)
	return orderSn
}

func (o *OrderServer) CartItemList(ctx context.Context, req *proto.UserInfo) (*proto.CartItemListResponse, error) {

	var cartItermListResponse proto.CartItemListResponse
	var cartItermList []model.ShoppingCart
	if result := global.DB.Model(&model.ShoppingCart{}).Where("User = ?", req.Id).Find(&cartItermList); result.Error != nil {
		return nil, status.Error(codes.Internal, result.Error.Error())
	} else {
		cartItermListResponse.Total = int32(result.RowsAffected)
	}
	for _, cart := range cartItermList {

		cartItermListResponse.Data = append(cartItermListResponse.Data, &proto.ShopCartInfoResponse{
			Id:      cart.ID,
			UserId:  cart.User,
			GoodsId: cart.Goods,
			Nums:    cart.Nums,
			Checked: cart.Checked,
		})
	}
	return &cartItermListResponse, nil
}

func (*OrderServer) CreateCartItem(ctx context.Context, req *proto.CartItemRequest) (*proto.ShopCartInfoResponse, error) {
	//将商品添加购物车
	var shoppingCart model.ShoppingCart
	if result := global.DB.Model(&model.ShoppingCart{}).Where("goods = ?", req.GoodsId).Find(&shoppingCart); result.RowsAffected > 0 {
		//2如果有的话就数量加1
		shoppingCart.Nums += req.Nums
	} else {
		//1.如果没有就新建
		shoppingCart.User = req.UserId
		shoppingCart.Goods = req.GoodsId
		shoppingCart.Nums = req.Nums
		shoppingCart.Checked = req.Checked
	}
	if result := global.DB.Save(&shoppingCart); result.RowsAffected == 0 {
		return nil, status.Error(codes.Internal, "商品添加失败")
	}

	return &proto.ShopCartInfoResponse{Id: shoppingCart.ID}, nil
}
func (*OrderServer) UpdateCartItem(ctx context.Context, req *proto.CartItemRequest) (*emptypb.Empty, error) {
	//查询数据是否存在
	var shoppingCart model.ShoppingCart
	if result := global.DB.Where("goods=? and user=?", req.GoodsId, req.UserId).Find(&shoppingCart); result.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "商品记录不存在")
	}
	//1.判断是否要改num
	if req.Nums > 0 {
		shoppingCart.Nums = req.Nums
	}
	shoppingCart.Checked = req.Checked
	if result := global.DB.Save(shoppingCart); result.Error != nil {
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	return &emptypb.Empty{}, nil
}
func (*OrderServer) DeleteCartItem(ctx context.Context, req *proto.CartItemRequest) (*emptypb.Empty, error) {

	if result := global.DB.Where("goods=? and user=?", req.GoodsId, req.UserId).Delete(&model.ShoppingCart{}); result.RowsAffected == 0 {
		return &emptypb.Empty{}, status.Error(codes.NotFound, "数据不存在")
	}
	return &emptypb.Empty{}, nil
}

type OrderListener struct {
	Code        codes.Code
	Detail      string
	ID          int32
	OrderAmount float32
	Ctx         context.Context
}

func (l OrderListener) ExecuteLocalTransaction(msg *primitive.Message) primitive.LocalTransactionState {

}

func (l OrderListener) CheckLocalTransaction(msg *primitive.MessageExt) primitive.LocalTransactionState {

}

func (*OrderServer) CreateOrder(ctx context.Context, req *proto.OrderRequest) (*proto.OrderInfoResponse, error) {
	/*
		新建订单
			1. 从购物车中获取到选中的商品
			2. 商品的价格自己查询 - 访问商品服务 (跨微服务)
			3. 库存的扣减 - 访问库存服务 (跨微服务)
			4. 订单的基本信息表 - 订单的商品信息表
			5. 从购物车中删除已购买的记录
	*/
	orderListener := OrderListener{Ctx: ctx}
	transactionProducer, err := rocketmq.NewTransactionProducer(
		&orderListener,
		producer.WithNameServer([]string{"10.26.10.121:9876"}),
	)
	if err != nil {
		zap.S().Errorf("生成producer失败: %s", err.Error())
		return nil, err
	}
	if err = transactionProducer.Start(); err != nil {
		zap.S().Errorf("启动producer失败: %s", err.Error())
		return nil, err
	}
	orderInfo := model.OrderInfo{
		User:         req.UserId,
		OrderSn:      GenerateOrderSn(req.UserId),
		Address:      req.Address,
		SignerName:   req.Name,
		SingerMobile: req.Mobile,
	}
	//应该在消息中具体指明一个订单的具体的商品的扣减情况
	jsonString, err := json.Marshal(orderInfo)
	message := primitive.NewMessage("order", jsonString)
	_, err = transactionProducer.SendMessageInTransaction(context.Background(), message)
	if err != nil {
		zap.S().Errorf("数据发送失败")
	}
	if err != nil {
		return nil, err
	}
	return &proto.OrderInfoResponse{Id: 1}, nil
}

func (*OrderServer) CreateOrder1(ctx context.Context, req *proto.OrderRequest) (*proto.OrderInfoResponse, error) {

	//事务开启
	tx := global.DB.Begin()
	// 1.从购物车中获取选中的商品
	var shopCart []model.ShoppingCart
	if result := tx.Model(&model.ShoppingCart{}).
		Where(&model.ShoppingCart{Checked: true, User: req.UserId}).
		Find(&shopCart); result.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "没有要结算的商品")
	}
	//2.从商品服务获取商品信息 调用商品服务
	var goodsIds []int32
	var goodsNums map[int32]int32
	for _, cart := range shopCart {
		goodsIds = append(goodsIds, cart.Goods)
		goodsNums[cart.Goods] = cart.Nums
	}
	goodsListResponse, err := global.GoodsSrv.BatchGetGoods(context.Background(), &proto.BatchGoodsIdInfo{Id: goodsIds})
	if err != nil {
		return nil, status.Error(codes.NotFound, "商品信息不存在")
	}
	//3.扣减库存	调用库存服务
	var goodsInvInfoList []*proto.GoodsInvInfo
	var orderAmount float32
	for _, goods := range goodsListResponse.Data {
		goodsInvInfo := proto.GoodsInvInfo{
			GoodsId: goods.Id,
			Num:     goodsNums[goods.Id],
		}
		goodsInvInfoList = append(goodsInvInfoList, &goodsInvInfo)
		orderAmount += float32(goodsNums[goods.Id]) * goods.ShopPrice
	}
	orderSn := GenerateOrderSn(req.UserId)
	if _, err = global.InventorySrv.Sell(context.Background(), &proto.SellInfo{
		GoodsInfo: goodsInvInfoList,
		OrderSn:   orderSn,
	}); err != nil {
		return nil, status.Error(codes.Internal, err.Error())
	}
	//4.生成订单基本信息
	orderInfo := model.OrderInfo{
		User:         req.UserId,
		OrderSn:      orderSn,
		Status:       "PAYING",
		OrderMount:   orderAmount,
		Address:      req.Address,
		SignerName:   req.Name,
		SingerMobile: req.Mobile,
		Post:         req.Post,
	}
	if result := tx.Save(&orderInfo); result.Error != nil {
		tx.Rollback()
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	//5.生成订单商品信息
	var orderGoodsList []model.OrderGoods
	for _, goods := range goodsListResponse.Data {
		orderGoods := model.OrderGoods{
			Order:      orderInfo.ID,
			Goods:      goods.Id,
			GoodsName:  goods.Name,
			GoodsImage: goods.GoodsFrontImage,
			GoodsPrice: goods.ShopPrice,
			Nums:       goodsNums[goods.Id],
		}
		orderGoodsList = append(orderGoodsList, orderGoods)
	}
	if result := tx.CreateInBatches(&orderGoodsList, 100); result.Error != nil {
		tx.Rollback()
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	//6.从购物车删除商品信息
	if result := tx.Where(&model.ShoppingCart{Checked: true, User: req.UserId}).Delete(&model.ShoppingCart{}); result.Error != nil {
		tx.Rollback()
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	tx.Commit()
	return &proto.OrderInfoResponse{Id: orderInfo.ID, OrderSn: orderSn}, nil
}
func (*OrderServer) OrderList(ctx context.Context, req *proto.OrderFilterRequest) (*proto.OrderListResponse, error) {

	var orderListResponse proto.OrderListResponse
	var orderList []model.OrderInfo
	var total int64
	if result := global.DB.Model(model.OrderInfo{}).Where(model.OrderInfo{User: req.UserId}).Count(&total); result.Error != nil {
		return nil, status.Error(codes.Internal, result.Error.Error())
	} else {
		orderListResponse.Total = int32(total)
	}
	if result := global.DB.Scopes(Paginate(int(req.Pages), int(req.PagePerNums))).Model(&model.OrderInfo{}).
		Where(&model.OrderInfo{User: req.UserId}).Find(&orderList); result.Error != nil {
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	for _, order := range orderList {
		orderInfoResponse := proto.OrderInfoResponse{
			Id:      order.ID,
			UserId:  order.User,
			OrderSn: order.OrderSn,
			PayType: order.PayType,
			Status:  order.Status,
			Post:    order.Post,
			Total:   order.OrderMount,
			Address: order.Address,
			Name:    order.SignerName,
			Mobile:  order.SingerMobile,
			AddTime: order.CreatedAt.Format("2006-01-02 15:04:04"),
		}
		orderListResponse.Data = append(orderListResponse.Data, &orderInfoResponse)
	}
	return &orderListResponse, nil
}
func (*OrderServer) OrderDetail(ctx context.Context, req *proto.OrderRequest) (*proto.OrderInfoDetailResponse, error) {

	//这个订单的id是否是当前用户的订单， 如果在web层用户传递过来一个id的订单， web层应该先查询一下订单id是否是当前用户的
	//在个人中心可以这样做，但是如果是后台管理系统，web层如果是后台管理系统 那么只传递order的id，如果是电商系统还需要一个用户的id
	var orderInfoDetailResponse proto.OrderInfoDetailResponse
	//1查询订单信息
	var orderInfo model.OrderInfo
	if result := global.DB.Model(&model.OrderInfo{}).
		Where(&model.OrderInfo{User: req.UserId, BaseModel: model.BaseModel{ID: req.Id}}).First(&orderInfo); result.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "订单信息不存在")
	}
	orderInfoDetailResponse.OrderInfo = &proto.OrderInfoResponse{
		Id:      orderInfo.ID,
		UserId:  orderInfo.User,
		OrderSn: orderInfo.OrderSn,
		PayType: orderInfo.PayType,
		Status:  orderInfo.Status,
		Post:    orderInfo.Post,
		Total:   orderInfo.OrderMount,
		Address: orderInfo.Address,
		Name:    orderInfo.SignerName,
		Mobile:  orderInfo.SingerMobile,
		AddTime: orderInfo.CreatedAt.String(),
	}
	//2.查询订单关联的商品  会涉及到更新商品信息的问题 会用到分布式事务
	var orderGoodsList []model.OrderGoods
	if result := global.DB.Model(&model.OrderGoods{}).
		Where(&model.OrderGoods{Order: orderInfo.ID}).Find(&orderGoodsList); result.Error != nil {
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	for _, goods := range orderGoodsList {
		orderItemResponse := proto.OrderItemResponse{
			Id:         goods.ID,
			OrderId:    goods.Order,
			GoodsId:    goods.Goods,
			GoodsName:  goods.GoodsName,
			GoodsImage: goods.GoodsImage,
			GoodsPrice: goods.GoodsPrice,
			Nums:       goods.Nums,
		}
		orderInfoDetailResponse.Goods = append(orderInfoDetailResponse.Goods, &orderItemResponse)
	}
	return &orderInfoDetailResponse, nil
}

func (*OrderServer) UpdateOrderStatus(ctx context.Context, req *proto.OrderStatus) (*emptypb.Empty, error) {

	if result := global.DB.Model(&model.OrderInfo{}).Where(&model.OrderInfo{OrderSn: req.OrderSn}).Update("status", req.Status); result.RowsAffected == 0 {
		return nil, status.Error(codes.Internal, "修改失败")
	}
	return &emptypb.Empty{}, nil
}
